using System.Security.Cryptography;
using System.Text;

namespace Project_Schedule.Model;

public static class RsaEncryptUtil
{
    /// <summary>
    /// 私钥
    /// </summary>
    public static string? PriPkcs8 { get; set; }

    /// <summary>
    /// 公钥
    /// </summary>
    public static string? Pub { get; set; }

    #region 标准的-公钥加密-私钥解密

    /** 默认编码字符集 */
    private static string DEFAULT_CHARSET = "UTF-8";

    /// <summary>
    /// 公钥加密（超过 私钥长度 / 8 - 11，分段加密）
    /// </summary>
    /// <param name="content">明文</param>
    /// <param name="charset">编码</param>
    /// <param name="publicKeyPem">公钥</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public static string RSAEncrypt(string content,
        string charset = "UTF-8")
    {
        string publicKeyPem = Pub!;
        try
        {
            publicKeyPem = publicKeyPem.Replace("-----BEGIN PUBLIC KEY-----", "")
                .Replace("-----END PUBLIC KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();

            RSA rsa = RSA.Create();
            rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKeyPem), out _);

            if (string.IsNullOrEmpty(charset))
            {
                charset = DEFAULT_CHARSET;
            }

            byte[] data = Encoding.GetEncoding(charset).GetBytes(content);
            int maxBlockSize = rsa.KeySize / 8 - 11; //加密块最大长度限制
            if (data.Length <= maxBlockSize)
            {
                byte[] cipherbytes = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
                return Convert.ToBase64String(cipherbytes);
            }

            MemoryStream plaiStream = new MemoryStream(data);
            MemoryStream crypStream = new MemoryStream();
            byte[] buffer = new byte[maxBlockSize];
            int blockSize = plaiStream.Read(buffer, 0, maxBlockSize);
            while (blockSize > 0)
            {
                byte[] toEncrypt = new byte[blockSize];
                Array.Copy(buffer, 0, toEncrypt, 0, blockSize);
                byte[] cryptograph = rsa.Encrypt(toEncrypt, RSAEncryptionPadding.Pkcs1);
                crypStream.Write(cryptograph, 0, cryptograph.Length);
                blockSize = plaiStream.Read(buffer, 0, maxBlockSize);
            }

            return Convert.ToBase64String(crypStream.ToArray(), Base64FormattingOptions.None);
        }
        catch (Exception ex)
        {
            throw new Exception("EncryptContent = " + content + ",charset = " + charset, ex);
        }
    }

    /// <summary>
    /// 私钥解密（超过 私钥长度 / 8 - 11，分段加密）
    /// </summary>
    /// <param name="content">密文</param>
    /// <param name="charset">编码</param>
    /// <param name="privateKeyPem">私钥</param>
    /// <param name="keyFormat">私钥格式 PKCS1,PKCS8</param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public static string RSADecrypt(string content, string charset = "UTF-8",
        string keyFormat = "PKCS8")
    {
        string privateKeyPem = PriPkcs8!;
        try
        {
            privateKeyPem = privateKeyPem.Replace("-----BEGIN RSA PRIVATE KEY-----", "")
                .Replace("-----END RSA PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();
            privateKeyPem = privateKeyPem.Replace("-----BEGIN PRIVATE KEY-----", "")
                .Replace("-----END PRIVATE KEY-----", "").Replace("\r", "").Replace("\n", "").Trim();
            
            RSA rsaCsp = RSA.Create();
            if (keyFormat == "PKCS8")
                rsaCsp.ImportPkcs8PrivateKey(Convert.FromBase64String(privateKeyPem), out _);
            else if (keyFormat == "PKCS1")
                rsaCsp.ImportRSAPrivateKey(Convert.FromBase64String(privateKeyPem), out _);
            else
                throw new Exception("只支持PKCS8，PKCS1");

            if (string.IsNullOrEmpty(charset))
            {
                charset = DEFAULT_CHARSET;
            }

            byte[] data = Convert.FromBase64String(content);
            int maxBlockSize = rsaCsp.KeySize / 8; //解密块最大长度限制
            if (data.Length <= maxBlockSize)
            {
                byte[] cipherbytes = rsaCsp.Decrypt(data, RSAEncryptionPadding.Pkcs1);
                return Encoding.GetEncoding(charset).GetString(cipherbytes);
            }

            MemoryStream crypStream = new MemoryStream(data);
            MemoryStream plaiStream = new MemoryStream();
            byte[] buffer = new byte[maxBlockSize];
            int blockSize = crypStream.Read(buffer, 0, maxBlockSize);
            while (blockSize > 0)
            {
                byte[] toDecrypt = new byte[blockSize];
                Array.Copy(buffer, 0, toDecrypt, 0, blockSize);
                byte[] cryptograph = rsaCsp.Decrypt(toDecrypt, RSAEncryptionPadding.Pkcs1);
                plaiStream.Write(cryptograph, 0, cryptograph.Length);
                blockSize = crypStream.Read(buffer, 0, maxBlockSize);
            }

            return Encoding.GetEncoding(charset).GetString(plaiStream.ToArray());
        }
        catch (Exception ex)
        {
            throw new Exception("DecryptContent = " + content + ",charset = " + charset, ex);
        }
    }

    #endregion
}